home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C ++ / Frameworks / MacZoop 1.6.5 / More Classes / Advanced Dialogs / ZAdvancedDialog.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1997-07-14  |  33.9 KB  |  1,368 lines  |  [TEXT/CWIE]

  1. /*************************************************************************************************
  2. *
  3. *
  4. *            ObjectMacZapp        -- a standard Mac OOP application template
  5. *
  6. *
  7. *
  8. *            ZAdvancedDialog.cpp    -- a dialog box that can be extended with new types of items.
  9. *                                    this will handle list boxes without further subclassing.
  10. *
  11. *
  12. *            © 1997, Graham Cox
  13. *
  14. *
  15. *
  16. *************************************************************************************************/
  17.  
  18. #include    "ZAdvancedDialog.h"
  19. #include    "MacZoop.h"
  20. #include    "ZApplication.h"
  21. #include    "ZGrafState.h"
  22.  
  23. #include    <LowMem.h>
  24.  
  25. // note: if you don't have the extra dialog items stuff, comment out this include. The
  26. // code that makes use of it will be automatically compiled out.
  27.  
  28. #include    "ZExtraDialogItems.h"
  29.  
  30. // list object for dialog items:
  31.  
  32. #include    "ZObjectArray.cpp"
  33.  
  34. // global procedure is used in ZListDialogItem to handle searching lists for matching
  35. // user's keypresses to list items.
  36.  
  37. static pascal short    FindCellCompProc( Ptr dataA, Ptr dataB, short lenA, short lenB );
  38.  
  39. ListSearchUPP    gFindCellCompUPP = NewListSearchProc( FindCellCompProc );
  40.  
  41. // global commander 
  42.  
  43. extern ZCommander*        gCurHandler;
  44.  
  45.  
  46. // item objects:
  47. /*--------------------------------***  CONSTRUCTOR  ***---------------------------------*/
  48.  
  49.  
  50. ZDialogItem::ZDialogItem( ZDialog* aDialog, short item )
  51.     : ZCommander( aDialog )
  52. {
  53.     short        iType;
  54.     
  55.     FailNIL( aDialog );
  56.     
  57.     id = item;
  58.     
  59.     // by default, items based on ZDialogItem cannot have the keyboard focus, and
  60.     // will behave accordingly. If you have an item that can have the keyboard focus,
  61.     // set this member to TRUE in your constructor. See ZListDialogItem for example.
  62.     
  63.     canBeHandler = FALSE;
  64.     isHandler = FALSE;
  65.     
  66.     // get initial info from the dialog manager item
  67.     
  68.     GetDialogItem( aDialog->GetMacWindow(), item, &iType, &iHand, &bounds );
  69.  
  70.     // convert the original dialog manager type into our more
  71.     // "sophisticated" types.
  72.     
  73.     enabled = ((iType & itemDisable) == 0);
  74.     iType &= 0x7F;
  75.     
  76.     switch ( iType )
  77.     {
  78.         case statText:
  79.             type = typeStaticText;
  80.             break;
  81.         case editText:
  82.             type = typeEditText;
  83.             break;
  84.         case iconItem:
  85.             type = typeIcon;
  86.             break;
  87.         case picItem:
  88.             type = typePicture;
  89.             break;
  90.         case ctrlItem + btnCtrl:
  91.             type = typeButton;
  92.             break;
  93.         case ctrlItem + chkCtrl:
  94.             type = typeCheckBox;
  95.             break;
  96.         case ctrlItem + radCtrl:
  97.             type = typeRadioButton;
  98.             break;
  99.         case ctrlItem + resCtrl:
  100.             type = typeResControl;
  101.             break;
  102.         case userItem:
  103.             type = typeUserItem;
  104.             break;
  105.     }
  106. }
  107.  
  108. /*--------------------------------***  DRAWBORDER  ***----------------------------------*/
  109. /*    
  110. draws the default outline used to indicate keyboard focus
  111. ----------------------------------------------------------------------------------------*/
  112.  
  113. void    ZDialogItem::DrawBorder( Boolean bState )
  114. {
  115.     ZDialog* zd = (ZDialog*) GetBoss();
  116.     
  117.     // border only drawn if owning window is active
  118.     
  119.     if (((WindowPeek) zd->GetMacWindow())->hilited || !bState )
  120.     {
  121.         Rect    r = bounds;
  122.         
  123.         InsetRect( &r, -3, -3 );
  124.         PenSize( 2, 2 );
  125.         
  126.         if ( bState )
  127.             PenMode( patOr );
  128.         else
  129.             PenMode( patBic );
  130.             
  131.         FrameRect( &r );
  132.         PenNormal();
  133.     }
  134. }
  135.  
  136. /*-------------------------------***  BECOMEHANDLER  ***--------------------------------*/
  137. /*    
  138. this item should become the command handler, or relinquish back to its boss if false. This
  139. allows this item to have the keyboard focus, and draws the border accordingly.
  140. ----------------------------------------------------------------------------------------*/
  141.  
  142. void    ZDialogItem::BecomeHandler( Boolean isBecoming )
  143. {
  144.     if (( isBecoming != isHandler) && canBeHandler )
  145.     {
  146.         ZAdvancedDialog*    zd = (ZAdvancedDialog*) GetBoss();
  147.         
  148.         // we are becoming the handler for the owning dialog, so switch its
  149.         // handler member to this. This is done via a couple of messages to
  150.         // the dialog, which, as its boss, will always receive them even if
  151.         // not explicitly told to do so.
  152.         
  153.         if ( isBecoming )
  154.             SendMessage( ItemNowHandler, NULL );
  155.         else
  156.             SendMessage( ItemNoLongerHandler, NULL );
  157.         
  158.         isHandler = isBecoming;
  159.         
  160.         // only draw the hilite border if we're not the only item that can be the
  161.         // handler. If this is the only one, then we're always it!
  162.         
  163.         if ( zd->HasMultipleFoci())
  164.         {
  165.             zd->Focus();
  166.             DrawBorder( isHandler );
  167.         }
  168.     }
  169. }
  170.  
  171.  
  172. #pragma mark =============  ZListDialogItem  ==============
  173.  
  174. // listbox dialog item:
  175. /*--------------------------------***  CONSTRUCTOR  ***---------------------------------*/
  176.  
  177.  
  178. ZListDialogItem::ZListDialogItem( ZDialog* aDialog, const short item  )
  179.     : ZDialogItem( aDialog, item )
  180. {
  181.     theList = NULL;
  182.     type = typeListBox;
  183.     canBeHandler = TRUE;    // list boxes can handle keyboard focus
  184.     
  185.     // get the key threshold. This is double the delay time
  186.     // set by the user for "delay until repeat" in the global
  187.     // keyboard control panel, or two seconds, whichever is shorter.
  188.     
  189.     fThresh = MIN( 120, LMGetKeyThresh() * 2);
  190.     searchStr[0] = 0;
  191.     lastKeyTime = 0;
  192.     lastSel.h = lastSel.v = 0;
  193.     strListID = 0;
  194.     
  195.     fontSize = 0;
  196.     font = 0;
  197. }
  198.  
  199.  
  200. /*--------------------------------***  DESTRUCTOR  ***----------------------------------*/
  201.  
  202. ZListDialogItem::~ZListDialogItem()
  203. {
  204.     LDispose( theList );
  205. }
  206.  
  207. /*---------------------------------***  DRAWITEM  ***-----------------------------------*/
  208. /*    
  209.  
  210. draw this dialog item. This updates the list
  211. ----------------------------------------------------------------------------------------*/
  212.  
  213. void    ZListDialogItem::DrawItem()
  214. {
  215.     ZAdvancedDialog*    zd;
  216.     ZGrafState    zg;
  217.     
  218.     zd = (ZAdvancedDialog*) GetBoss();
  219.     
  220.     // if we are using a font different to the window, set that now
  221.     
  222.     short    saveFont, saveSize;
  223.     
  224.     SetUpFontForList( &saveFont, &saveSize );
  225.             
  226.     LUpdate( zd->GetMacWindow()->visRgn, theList );
  227.     PenNormal();
  228.     FrameRect( &bounds );
  229.     
  230.     if ( fontSize != 0 &&
  231.          font != 0 )
  232.     {
  233.         TextFont( saveFont );
  234.         TextSize( saveSize );
  235.     }
  236.     
  237.     // add greyscale effects if desired
  238.     
  239.     #ifdef _GREYSCALE_APPEARANCE
  240.     
  241.     FrameGrayRect( &bounds );
  242.  
  243.     #endif
  244.     
  245.     // draw a border if we are the handler and the owner can have more than
  246.     // just this item as the keyboard focus
  247.     
  248.     if ( isHandler && zd->HasMultipleFoci())
  249.         DrawBorder( TRUE );
  250. }
  251.  
  252.  
  253. /*---------------------------------***  CLICKITEM  ***----------------------------------*/
  254. /*    
  255.  
  256. pass mouse clicks to the dialog item (the list, in this case)
  257. ----------------------------------------------------------------------------------------*/
  258.  
  259. void    ZListDialogItem::ClickItem( const Point where, const short modifiers )
  260. {
  261.     // the user clicked in this list box. If we are not already the handler, make
  262.     // sure that we are switched in. This will give us the keyboard focus and
  263.     // draw the hilited border if necessary
  264.     
  265.     Boolean    dblClick;
  266.     
  267.     if (! isHandler && canBeHandler)
  268.         BecomeHandler( TRUE );
  269.     
  270.     // if we are using a font different to the window, set that now
  271.     
  272.     short    saveFont, saveSize;
  273.     
  274.     SetUpFontForList( &saveFont, &saveSize );
  275.         
  276.     // now pass the click on to the list manager
  277.         
  278.     Cell a = { 0, 0 }, b;
  279.     
  280.     b = a;
  281.     
  282.     LGetSelect( TRUE, &a, theList );
  283.     dblClick = LClick( where, modifiers, theList );
  284.     LGetSelect( TRUE, &b, theList );
  285.     
  286.     if ( fontSize != 0 &&
  287.          font != 0 )
  288.     {
  289.         TextFont( saveFont );
  290.         TextSize( saveSize );
  291.     }
  292.         
  293.     // if the click resulted in a change to the selection in the list, send a message
  294.     // to tell anyone whose listening what the new cell is. This is automatically
  295.     // picked up by the owning dialog, which makes it very easy to respond to listbox
  296.     // clicks and do whatever you need to with the info.
  297.     
  298.     if ( DeltaPoint( a, b ))
  299.         SendMessage( newListItemSelected, &b );
  300.         
  301.     if ( dblClick )
  302.         SendMessage( listItemDoubleClicked, &b );
  303. }
  304.  
  305.  
  306. /*-------------------------------***  BECOMEHANDLER  ***--------------------------------*/
  307. /*    
  308. change activation state of list according to whether this has focus or not
  309. ----------------------------------------------------------------------------------------*/
  310.  
  311. void    ZListDialogItem::BecomeHandler( Boolean isBecoming )
  312. {
  313.     inherited::BecomeHandler( isBecoming );
  314.     
  315.     // we don't deactivate or activate the whole list, since the appearance isn't too
  316.     // great and is inconsistent with e.g. the Standard File Dialog. Instead we save
  317.     // the current hilited item and save and restore that.
  318.     
  319.     ActivateListItem( isHandler );    
  320. }
  321.  
  322.  
  323. /*-----------------------------------***  TYPE  ***-------------------------------------*/
  324. /*    
  325. when we have the keyboard focus, this will be called and we can use it to navigate
  326. around our list. This also selects cells according to what the user types. NOTE that
  327. this only works if the list is ordered alphabetically in the first place.
  328. ----------------------------------------------------------------------------------------*/
  329.  
  330. void    ZListDialogItem::Type( const char theKey )
  331. {
  332.     Cell    curCell = { 0, 0 }, oldCell;
  333.     Rect    db = (*theList)->dataBounds;
  334.     
  335.     db.right--;
  336.     db.bottom--;
  337.     
  338.     // find the currently selected cell
  339.     
  340.     LGetSelect( TRUE, &curCell, theList );
  341.     oldCell = curCell;
  342.     
  343.     switch ( theKey )
  344.     {
  345.         case LEFT_ARROW_KEY:        // left arrow, move left if possible
  346.             curCell.h = MAX( db.left, curCell.h - 1 );
  347.             break;
  348.         case RIGHT_ARROW_KEY:        // right arrow, move right if possible
  349.             curCell.h = MIN( db.right, curCell.h + 1 );
  350.             break;
  351.         case UP_ARROW_KEY:            // up arrow, move up if possible
  352.             curCell.v = MAX( db.top, curCell.v - 1 );
  353.             break;
  354.         case DOWN_ARROW_KEY:        // down arrow, move down if possible
  355.             curCell.v = MIN( db.bottom, curCell.v + 1 );
  356.             break;
  357.         default:
  358.             
  359.             // handle letter typing to select items in list.            
  360.             
  361.             if (( theKey >= 'a' && theKey <= 'z' ) ||
  362.                 ( theKey >= 'A' && theKey <= 'Z' ))
  363.             {
  364.                 long    theTime = TickCount();
  365.                 Cell    findCell = { 0, 0 };
  366.                 
  367.                 // time to start a new string yet?
  368.                 
  369.                 if ((theTime > lastKeyTime + fThresh) || (searchStr[0] > 12))
  370.                 {
  371.                     // start a new string
  372.                     
  373.                     searchStr[0] = 0;
  374.                 }
  375.                 lastKeyTime = theTime;
  376.                 
  377.                 // add typed chars to the search string
  378.                 
  379.                 searchStr[++searchStr[0]] = theKey;
  380.                 
  381.                 // find a cell that matches this
  382.                 
  383.                 if ( LSearch( &searchStr[1], searchStr[0], gFindCellCompUPP, &findCell, theList ))
  384.                 {
  385.                     // we found one, so make it the selected cell
  386.                     
  387.                     curCell = findCell;
  388.                 }
  389.             }
  390.             break;
  391.     }
  392.     
  393.     // turn off the old cell and hilite the new cell
  394.     
  395.     if ( DeltaPoint( curCell, oldCell ))
  396.     {
  397.         short    saveFont, saveSize;
  398.         
  399.         SetUpFontForList( &saveFont, &saveSize );
  400.         
  401.         LSetSelect( FALSE, oldCell, theList );
  402.         LSetSelect( TRUE, curCell, theList );
  403.         LAutoScroll( theList );
  404.         
  405.         if ( fontSize != 0 &&
  406.              font != 0 )
  407.         {
  408.             TextFont( saveFont );
  409.             TextSize( saveSize );
  410.         }
  411.         // tell listeners which cell we just selected
  412.         
  413.         SendMessage( newListItemSelected, &curCell );
  414.     }
  415.     
  416.     inherited::Type( theKey );
  417. }
  418.  
  419.  
  420. /*---------------------------------***  INITITEM  ***-----------------------------------*/
  421. /*    
  422. pass pseudo-parameters to this item for action
  423. ----------------------------------------------------------------------------------------*/
  424.  
  425. void    ZListDialogItem::InitItem( const long param1, const long param2 )
  426. {
  427.     // initialises this item from the pseudo-parameter list. The parameter list consists of
  428.     // of up to two numbers, but only this knows what they mean! For lists, the first value
  429.     // identifies the resource ID of a STR# resource which we use to fill the list. The second
  430.     // is unused at present. NOTE that caller will pass 0 for missing parameters, so generally
  431.     // this value should be avoided as having a special meaning.
  432.     
  433.     short    numItems, i, p2;
  434.     Cell    aCell = { 0, 0 };
  435.     Handle    strListH;
  436.     Str255    temp;
  437.     
  438.     // make the mac list manager structures based on the template <param1>
  439.     
  440.     MakeMacList( param1 );
  441.     
  442.     // if the template specified a string list, ignore param2. If it didn't,
  443.     // param2 is the ID of the STR# resource to use. If everything was 0,
  444.     // then we don't add anything to the list, and the caller must do this.
  445.     
  446.     if ( strListID > 0 )
  447.         p2 = strListID;
  448.     else
  449.         p2 = param2;
  450.     
  451.     if ( p2 > 0 )
  452.     {
  453.         strListH = GetResource( 'STR#', p2 );
  454.         
  455.         if ( strListH )
  456.         {
  457.             // how many items in the list?
  458.             
  459.             numItems = **(short**) strListH;
  460.             
  461.             // add each string to the list
  462.             
  463.             LSetDrawingMode( FALSE, theList );
  464.             aCell.v = (*theList)->dataBounds.bottom;
  465.             
  466.             for (i = 1; i <= numItems; i++ )
  467.             {
  468.                 GetIndString( temp, p2, i );
  469.                 
  470.                 // append to the list
  471.                 
  472.                 aCell.v = LAddRow( 1, aCell.v + 1, theList );
  473.                 LSetCell( &temp[1], temp[0], aCell, theList );
  474.             }
  475.             
  476.             // make the bounding box an exact number of cells high- this looks far neater
  477.             // and saves the user from having to worry about tweaking the item's bounds
  478.             // in the dialog template.
  479.             
  480.             short    cellHeight = (*theList)->cellSize.v;
  481.             short    adjustAmount;
  482.             Boolean    hasH, hasV;
  483.             
  484.             hasH = ((*theList)->hScroll != NULL);
  485.             hasV = ((*theList)->vScroll != NULL);
  486.             
  487.             adjustAmount = (bounds.bottom - bounds.top - (hasH? 17 : 2)) % cellHeight;
  488.             bounds.bottom -= adjustAmount;
  489.             
  490.             LSize( bounds.right - bounds.left - (hasV? 17 : 2),
  491.                    bounds.bottom - bounds.top - (hasH? 17 : 2),
  492.                    theList );
  493.             
  494.             // turn list drawing back on and get rid of the string list
  495.             
  496.             LSetDrawingMode( TRUE, theList );
  497.             ReleaseResource( strListH );
  498.         }
  499.     }    
  500. }
  501.  
  502.  
  503. /*-------------------------------***  MAKEMACLIST  ***----------------------------------*/
  504. /*    
  505. make the list manager list. This can be built either as a 1-column list (template == 0),
  506. or from a list template resource which is defined in the header.
  507. ----------------------------------------------------------------------------------------*/
  508.  
  509. void    ZListDialogItem::MakeMacList( const short listTemplateID )
  510. {
  511.     // make the list manager list. By default, the list is one column wide with no
  512.     // initial rows. It has a vertical scrollbar only, and the LDEF used is the standard
  513.     // system one which displays strings in chicago 12. If you supply a template resource
  514.     // the list can be built from that, giving full generality.
  515.     
  516.     ZDialog*            zd = (ZDialog*) GetBoss();
  517.     Rect                r, dataBounds = { 0, 0, 0, 1 };
  518.     Point                cellSize = { 0, 0 };
  519.     ListTemplateHdl        ltH;
  520.     Boolean                hasV = TRUE, hasH = FALSE, hasGrow = FALSE, isVis = TRUE;
  521.     short                procID = 0;
  522.     
  523.     // based on the original item's bounds
  524.     
  525.     r = bounds;
  526.     InsetRect( &r, 1, 1 );
  527.     
  528.     // obtain the template if any
  529.     
  530.     if ( listTemplateID != 0 )
  531.     {
  532.         ltH = ( ListTemplateHdl ) GetResource( kListTemplateResType, listTemplateID );
  533.     
  534.         if ( ltH )
  535.         {
  536.             // there is a template, so use that to define the list.
  537.         
  538.             dataBounds.right     = MAX((*ltH)->columns, 1);
  539.             dataBounds.bottom     = (*ltH)->rows;
  540.             procID                 = (*ltH)->procID;
  541.             hasV                 = (*ltH)->hasVertScroll;
  542.             hasH                 = (*ltH)->hasHorizScroll;
  543.             hasGrow             = (*ltH)->hasGrowBox;
  544.             isVis                 = (*ltH)->visible;
  545.             
  546.             // set other data members from the template
  547.             
  548.             canBeHandler         = (*ltH)->keyboardNav;
  549.             fontSize             = (*ltH)->fontSize;
  550.             strListID            = (*ltH)->stringsListID;
  551.             
  552.             GetFNum((*ltH)->fontName, &font );
  553.             
  554.             ReleaseResource((Handle) ltH );
  555.         }
  556.     }
  557.     
  558.     // tweak bounding box to allow for scrollbars we require
  559.     
  560.     if ( hasV || hasGrow )
  561.         r.right -= 15;
  562.         
  563.     if ( hasH || hasGrow )
  564.         r.bottom -= 15;
  565.         
  566.     // if the desired font is other than the default window font, set that
  567.     // now so that the cell size is calculated accordingly.
  568.     
  569.     short    saveFont, saveSize;
  570.     
  571.     SetUpFontForList( &saveFont, &saveSize );
  572.     
  573.     // make the List Manager list according to the template, or default
  574.     
  575.     theList = LNew( &r, &dataBounds,
  576.                     cellSize, procID,
  577.                     zd->GetMacWindow(),
  578.                     isVis, hasGrow, hasH, hasV );
  579.                     
  580.     FailNIL( theList );
  581.     LActivate( TRUE, theList );
  582.     ActivateListItem( FALSE );
  583.     
  584.     // but back font if we changed it
  585.     
  586.     if ( fontSize != 0 &&
  587.          font != 0 )
  588.     {
  589.         TextFont( saveFont );
  590.         TextSize( saveSize );
  591.     }
  592. }
  593.  
  594. /*----------------------------***  ACTIVATELISTITEM  ***--------------------------------*/
  595. /*    
  596. hilite current selection. Used instead of LActivate for showing state when focussed, etc.
  597. ----------------------------------------------------------------------------------------*/
  598.  
  599. void    ZListDialogItem::ActivateListItem( const Boolean state )
  600. {
  601.     short    saveFont, saveSize;
  602.     
  603.     SetUpFontForList( &saveFont, &saveSize );
  604.     
  605.     if ( state )
  606.         LSetSelect( TRUE, lastSel, theList );
  607.     else
  608.     {
  609.         lastSel.h = lastSel.v = 0;
  610.         LGetSelect(    TRUE, &lastSel, theList ); 
  611.         LSetSelect( FALSE, lastSel, theList );
  612.     }
  613.     
  614.     // but back font if we changed it
  615.     
  616.     if ( fontSize != 0 &&
  617.          font != 0 )
  618.     {
  619.         TextFont( saveFont );
  620.         TextSize( saveSize );
  621.     }
  622. }
  623.  
  624.  
  625. /*---------------------------------***  ACTIVATE  ***-----------------------------------*/
  626. /*    
  627. owning window is becoming active/inactive. Handle hiliting accordingly.
  628. ----------------------------------------------------------------------------------------*/
  629.  
  630. void    ZListDialogItem::Activate( const Boolean isActive )
  631. {
  632.     ZAdvancedDialog* zd = (ZAdvancedDialog*) GetBoss();
  633.     
  634.     if ( isHandler && zd->HasMultipleFoci())
  635.         DrawBorder( isActive );
  636.     
  637.     ActivateListItem( isActive );
  638.     PenNormal();
  639. }
  640.  
  641.  
  642. /*-----------------------------***  SETUPFONTFORLIST  ***-------------------------------*/
  643. /*    
  644. if list is using font different to window, this set it up. Should be called before any
  645. list manager call. Returns existing font
  646. ----------------------------------------------------------------------------------------*/
  647.  
  648. void    ZListDialogItem::SetUpFontForList( short* curFont, short* curSize )
  649. {
  650.     if ( font != 0 &&
  651.          fontSize != 0 )
  652.     {
  653.         ZDialog*    zd = (ZDialog*) GetBoss();
  654.         
  655.         *curFont = zd->GetMacWindow()->txFont;
  656.         *curSize = zd->GetMacWindow()->txSize;
  657.         
  658.         TextFont( font );
  659.         TextSize( fontSize );
  660.     }
  661. }
  662.  
  663.  
  664. /*-------------------------------***  APPENDSTRING  ***---------------------------------*/
  665. /*
  666. appends the passed string to the list. This is a convenience function for building lists
  667. on the fly programmatically. This also assumes a 1-column list.    
  668. ----------------------------------------------------------------------------------------*/
  669.  
  670. void    ZListDialogItem::AppendString( Str255 aString, Boolean drawIt )
  671. {
  672.     Cell    aCell = { 0, 0 };
  673.     
  674.     aCell.v = (*theList)->dataBounds.bottom;
  675.     
  676.     LSetDrawingMode( FALSE, theList );
  677.  
  678.     // append to the list
  679.         
  680.     aCell.v = LAddRow( 1, aCell.v + 1, theList );
  681.     LSetCell( &aString[1], aString[0], aCell, theList );
  682.     
  683.     LSetDrawingMode( TRUE, theList );
  684.     
  685.     if ( drawIt )
  686.         DrawItem();
  687. }
  688.  
  689.  
  690. #pragma mark ============  FindCellCompProc  ==============
  691.  
  692. /*----------------------------***  FindCellCompProc  ***--------------------------------*/
  693. /*    
  694. used to locate suitable cells when typing in a list box
  695. ----------------------------------------------------------------------------------------*/
  696.  
  697. static pascal short    FindCellCompProc( Ptr dataA, Ptr dataB, short lenA, short lenB )
  698. {
  699.     short    result = 1;
  700.     
  701.     if ( lenA > 0 )
  702.     {
  703.         if ( IUMagIDString( dataA, dataB, lenA, lenB ) == 0 )
  704.             result = 0;
  705.         else
  706.         {
  707.             if ( IUMagString( dataA, dataB, lenA, lenB ) == 1)
  708.                 result = 0;
  709.         }    
  710.     }
  711.     
  712.     return result;
  713. }
  714.  
  715. #pragma mark ============  ZAdvancedDialog  ==============
  716.  
  717. // the dialog:
  718. /*--------------------------------***  CONSTRUCTOR  ***---------------------------------*/
  719.  
  720. ZAdvancedDialog::ZAdvancedDialog( ZCommander* aBoss, const short dialogID )
  721.     : ZDialog( aBoss, dialogID )
  722. {
  723.     itsItems = NULL;    // no special items initially
  724.     curHandler = NULL;    // interpreted to mean "this", if NULL
  725.     focusItem = 0;        // no object item has the keyboard focus
  726. }
  727.  
  728.  
  729. /*---------------------------------***  DESTRUCTOR  ***---------------------------------*/
  730.  
  731. ZAdvancedDialog::~ZAdvancedDialog()
  732. {
  733.     if ( itsItems )
  734.     {
  735.         itsItems->DisposeAll();
  736.         ForgetObject( itsItems );
  737.     }
  738. }
  739.  
  740.  
  741. /*----------------------------------***  SETUP  ***-------------------------------------*/
  742. /*    
  743. set up the dialog, including special object items
  744. ----------------------------------------------------------------------------------------*/
  745.  
  746. void    ZAdvancedDialog::SetUp()
  747. {
  748.     inherited::SetUp();
  749.     
  750.     // once everything is built in the normal way, look for the magic static text items.
  751.     
  752.     Focus();
  753.     BuildItemList();    
  754.     
  755.     // if we only have a single focus, make sure it's selected by default! Otherwise we
  756.     // rely on the inherited SetUp to select the first editable text field. You can of
  757.     // course override this to make any desired item the initial focus.
  758.     
  759.     if ( ! HasMultipleFoci())
  760.         SelectNextFocus();
  761. }
  762.  
  763.  
  764. /*-------------------------------***  SELECTITEM  ***-----------------------------------*/
  765. /*    
  766. for your convenience, you can call this to switch to a particular edit field or other
  767. focussable item. Called internally by SelectNextFocus. If the item can't be a focus,
  768. this does nothing. This will redraw hilites, etc as needed.
  769. ----------------------------------------------------------------------------------------*/
  770.  
  771. void    ZAdvancedDialog::SelectItem( const short item )
  772. {
  773.     ZDialogItem*    di;
  774.     short            iType;
  775.     Handle            iHand;
  776.     Rect            iBox;
  777.     
  778.     di = GetObjectItem( item );
  779.     
  780.     if (di && di->CanBeHandler())
  781.         di->BecomeHandler( TRUE );
  782.     else
  783.     {
  784.         GetDialogItem( macWindow, item, &iType, &iHand, &iBox );
  785.         
  786.         if (( iType & 0x7F) == editText )
  787.         {
  788.             focusItem = 0;
  789.             ClickItem( item );
  790.         }
  791.     }
  792. }
  793.  
  794.  
  795. /*--------------------------------***  DEACTIVATE  ***----------------------------------*/
  796. /*    
  797. deactivate focussed item if needed
  798. ----------------------------------------------------------------------------------------*/
  799.  
  800. void    ZAdvancedDialog::Deactivate()
  801. {
  802.     if ( focusItem != 0 )
  803.     {
  804.         // one of our items is the focus, not a text field, so handle the hilite
  805.         // here and return handled TRUE.
  806.         
  807.         ZDialogItem*    di = GetObjectItem( focusItem );
  808.         
  809.         if ( di )
  810.             di->Activate( FALSE );
  811.     } 
  812.     else
  813.         inherited::Deactivate();
  814. }
  815.  
  816.  
  817. /*---------------------------------***  ACTIVATE  ***-----------------------------------*/
  818. /*    
  819. activate focussed item if needed
  820. ----------------------------------------------------------------------------------------*/
  821.  
  822. void    ZAdvancedDialog::Activate()
  823. {
  824.     if ( focusItem != 0 )
  825.     {
  826.         // one of our items is the focus, not a text field, so handle the hilite
  827.         // here and return handled TRUE.
  828.         
  829.         ZDialogItem*    di = GetObjectItem( focusItem );
  830.         
  831.         if ( di )
  832.             di->Activate( TRUE );
  833.     }
  834.     else
  835.         inherited::Activate();
  836. }
  837.  
  838.  
  839. /*--------------------------------***  GETHANDLER  ***----------------------------------*/
  840. /*    
  841. return active item if current, or this
  842. ----------------------------------------------------------------------------------------*/
  843.  
  844. ZCommander*        ZAdvancedDialog::GetHandler()
  845. {
  846.     if ( curHandler )
  847.         return curHandler;
  848.     else
  849.         return this;
  850. }
  851.  
  852.  
  853. /*--------------------------------***  CLICKITEM  ***-----------------------------------*/
  854. /*    
  855. pass clicks to dialog items
  856. ----------------------------------------------------------------------------------------*/
  857.  
  858. void    ZAdvancedDialog::ClickItem( const short item )
  859. {
  860.     ZDialogItem*    di;
  861.     
  862.     di = GetObjectItem( item );
  863.     
  864.     if ( di && di->Enabled())
  865.     {
  866.         EventRecord    theEvent;
  867.         
  868.         // get the mouse and modifiers from the most recent event
  869.         
  870.         gApplication->GetCurrentEvent( &theEvent );
  871.         GlobalToLocal( &theEvent.where );
  872.         
  873.         di->ClickItem( theEvent.where, theEvent.modifiers );
  874.     }
  875.     else
  876.     {
  877.         // if the item is an edit text field, then we may need to divert the keyboard focus
  878.         
  879.         short    iType;
  880.         Handle    iHand;
  881.         Rect    iBox;
  882.         
  883.         GetDialogItem( macWindow, item, &iType, &iHand, &iBox );
  884.         if ((iType & 0x7F) == editText )
  885.             ReceiveMessage( NULL, ItemNowHandler, NULL );
  886.             
  887.         inherited::ClickItem( item );
  888.     }
  889. }
  890.  
  891.  
  892. /*-----------------------------------***  TYPE  ***-------------------------------------*/
  893. /*    
  894. handle keypresses. We need to do a little more than usual to handle tabbing, and passing
  895. keystrokes to our custom items, if need be
  896. ----------------------------------------------------------------------------------------*/
  897.  
  898. void    ZAdvancedDialog::Type( const char theKey )
  899. {
  900.     Boolean        handled = FALSE;
  901.     
  902.     Focus();
  903.     
  904.     if ( theKey == TAB_KEY )        // tab
  905.         handled = SelectNextFocus();
  906.         
  907.     if (! handled && ( curHandler == NULL ))
  908.         inherited::Type( theKey );
  909. }
  910.  
  911.  
  912. /*-------------------------------***  ADJUSTCURSOR  ***---------------------------------*/
  913. /*    
  914. if the cursor is over an object item, call its adjust cursor method
  915. ----------------------------------------------------------------------------------------*/
  916.  
  917. void    ZAdvancedDialog::AdjustCursor( const Point mouse, const short modifiers )
  918. {
  919.     ZDialogItem*    di;
  920.     
  921.     di = FindObjectItem( mouse );
  922.     
  923.     if (di && di->Enabled())
  924.         di->AdjustCursor( mouse, modifiers );
  925.     else
  926.         inherited::AdjustCursor( mouse, modifiers );
  927. }
  928.  
  929.  
  930. /*-------------------------------***  DRAWUSERITEM  ***---------------------------------*/
  931. /*    
  932. if the item is a dialog item object, ask it to draw, otherwise call the inherited method
  933. ----------------------------------------------------------------------------------------*/
  934.  
  935. void    ZAdvancedDialog::DrawUserItem( const short item )
  936. {
  937.     ZDialogItem*    di;
  938.     
  939.     di = GetObjectItem( item );
  940.     
  941.     if ( di )
  942.     {
  943.         ZGrafState    zg;
  944.         
  945.         di->DrawItem();
  946.     }
  947.     else
  948.         inherited::DrawUserItem( item );
  949. }
  950.  
  951.  
  952. /*------------------------***  PARSESTATTEXTPSEUDOOBJECTS  ***--------------------------*/
  953. /*    
  954. Extract parameters from the static text string
  955. ----------------------------------------------------------------------------------------*/
  956.  
  957. void    ZAdvancedDialog::ParseStatTextPseudoObjects( Str255 sText, long* typeParam, long* param1, long* param2 )
  958. {
  959.     // initially assume we won't find the magic string:
  960.     
  961.     *typeParam = 0;
  962.     *param1 = 0;
  963.     *param2 = 0;
  964.     
  965.     // is the string at least 6 chars long? It must consist of two dollar signs followed
  966.     // by a four character object code. The additional parameters are optional.
  967.     
  968.     if ( sText[0] >= 6 )
  969.     {
  970.         // does it begin with two dollar signs?
  971.         
  972.         if (sText[1] == '$' &&
  973.             sText[2] == '$' )
  974.         {
  975.             // yes, so extract the magic object code ( next 4 chars ). Note that heavyweight
  976.             // frameworks sometimes do this sort of thing, but with the actual class name,
  977.             // which they then pass to new_by_name, or somesuch. Though that allows greater
  978.             // generality with less code, it is not really supported in a nice generic way
  979.             // by all compilers, so we have opted for this simpler approach.
  980.     
  981.             *typeParam = *(long*) &sText[3];
  982.             
  983.             // any parameters? These should be comma delimited numbers
  984.             
  985.             char    pc = 0;
  986.             char    i = 7;
  987.             char    t;
  988.             long    p;
  989.             Str15    seg;
  990.             
  991.             while( i <= sText[0] )
  992.             {
  993.                 if ( sText[i++] == ',' )
  994.                 {
  995.                     t = i;
  996.                     
  997.                     // found a comma, search forward to next comma or
  998.                     // end of string
  999.                     
  1000.                     while(( sText[t] != ',' ) && ( t <= sText[0] )) t++;
  1001.                     
  1002.                     // extract part of string between two commas
  1003.                     
  1004.                     BlockMoveData( &sText[i], &seg[1], t - i );
  1005.                     seg[0] = t - i;
  1006.                     
  1007.                     StringToNum( seg, &p );
  1008.                     
  1009.                     i = t;
  1010.                     
  1011.                     // set parameter
  1012.                     
  1013.                     switch (pc++)
  1014.                     {
  1015.                         case 0:
  1016.                             *param1 = p;
  1017.                             break;
  1018.                         case 1:
  1019.                             *param2 = p;
  1020.                             break;
  1021.                     }
  1022.                 }
  1023.             }
  1024.         }
  1025.     }
  1026. }
  1027.  
  1028. /*-------------------------------***  BUILDITEMLIST  ***--------------------------------*/
  1029. /*    
  1030. builds the list of object items. This scans the dialog items looking for static text items.
  1031. If they contain the magic strings, the items are converted to user items and a relevant
  1032. object is created. To extend the types of objects this can make, override the
  1033. MakeObjectItem method
  1034. ----------------------------------------------------------------------------------------*/
  1035.  
  1036. void    ZAdvancedDialog::BuildItemList()
  1037. {
  1038.     short            n = CountDITL( macWindow );
  1039.     short            iType;
  1040.     long            p0, p1, p2;
  1041.     Handle            iHand;
  1042.     Rect            iBox;
  1043.     ZDialogItem*    di;
  1044.     Str255            iText;
  1045.     
  1046.     do
  1047.     {
  1048.         GetDialogItem( macWindow, n, &iType, &iHand, &iBox );
  1049.         
  1050.         if ((iType & 0x7F) == statText)
  1051.         {
  1052.             GetDialogItemText( iHand, iText );
  1053.         
  1054.             // attempt to extract "magic" object parameters
  1055.             
  1056.             ParseStatTextPseudoObjects( iText, &p0, &p1, &p2 );
  1057.             
  1058.             // call the method to make the relevant object item from the code passed
  1059.             
  1060.             di = MakeObjectItem( p0, n );
  1061.             
  1062.             if ( di )
  1063.             {
  1064.                 // if the item list hasn't been created yet, do it now
  1065.                 
  1066.                 if ( itsItems == NULL )
  1067.                     FailNIL( itsItems = new ZDialogItemList());
  1068.                     
  1069.                 // convert the static text item to a user item and set up its
  1070.                 // drawing proc.
  1071.                 
  1072.                 SetDialogItem( macWindow, n, userItem, (Handle) gUIVectorUPP, &iBox );
  1073.             
  1074.                 // add it to our list of items
  1075.                 
  1076.                 itsItems->AppendItem( di );
  1077.             
  1078.                 // now pass it any parameters we may have extracted
  1079.                 
  1080.                 di->InitItem( p1, p2 );
  1081.             }
  1082.         }
  1083.     }
  1084.     while( --n );
  1085. }
  1086.  
  1087.  
  1088. /*-----------------------------***  MAKEOBJECTITEM  ***---------------------------------*/
  1089. /*    
  1090. make an object to match the type sent from the stat text parameter. Override for
  1091. additional types. In fact, this is the only method you need to override to extend the
  1092. number of special objects this dialog can deal with.
  1093. ----------------------------------------------------------------------------------------*/
  1094.  
  1095. ZDialogItem*    ZAdvancedDialog::MakeObjectItem( const long aType, const short id )
  1096. {
  1097.     ZDialogItem*    di = NULL;
  1098.     
  1099.     switch ( aType )
  1100.     {
  1101.         case 'LIST':
  1102.             di = new ZListDialogItem( this, id );
  1103.             break;
  1104.         
  1105.         #ifdef __ZEXTRADIALOGITEMS__
  1106.         
  1107.         case 'LINE':
  1108.             di = new ZLineDialogItem( this, id );
  1109.             break;
  1110.         case 'ICLB':
  1111.             di = new ZIconListBox( this, id );
  1112.             break;
  1113.         case 'TEXT':
  1114.             di = new ZScrollingTextBox( this, id );
  1115.             break;
  1116.         
  1117.         #endif
  1118.     }
  1119.  
  1120.     return di;
  1121. }
  1122.  
  1123.  
  1124. /*---------------------------------***  FINDITEM  ***-----------------------------------*/
  1125. /*    
  1126. find which if any of the special items contains the point
  1127. ----------------------------------------------------------------------------------------*/
  1128.  
  1129. ZDialogItem*    ZAdvancedDialog::FindObjectItem( const Point where )
  1130. {
  1131.     ZDialogItem*    di = NULL;
  1132.     long            i;
  1133.     
  1134.     if ( itsItems )
  1135.     {
  1136.         for( i = 1; i <= itsItems->CountItems(); i++ )
  1137.         {    
  1138.             di = itsItems->GetObject( i );
  1139.             
  1140.             if (di && di->ContainsPoint( where ))
  1141.                 break;
  1142.  
  1143.             di = NULL;
  1144.         }
  1145.     }
  1146.     
  1147.     return di;
  1148. }
  1149.  
  1150.  
  1151. /*----------------------------------***  GETITEM  ***-----------------------------------*/
  1152. /*    
  1153. find an object item that has the ID passed, or NULL if none do.
  1154. ----------------------------------------------------------------------------------------*/
  1155.  
  1156. ZDialogItem*    ZAdvancedDialog::GetObjectItem( const short item )
  1157. {
  1158.     ZDialogItem*    di = NULL;
  1159.     long            i;
  1160.     
  1161.     if ( itsItems )
  1162.     {
  1163.         for( i = 1; i <= itsItems->CountItems(); i++ )
  1164.         {    
  1165.             di = itsItems->GetObject( i );
  1166.             
  1167.             if (di && di->GetID() == item )
  1168.                 break;
  1169.  
  1170.             di = NULL;
  1171.         }
  1172.     }
  1173.     
  1174.     return di;
  1175. }
  1176.  
  1177.  
  1178. /*------------------------------***  RECEIVEMESSAGE  ***--------------------------------*/
  1179. /*    
  1180. manipulate command chain in response to items coming into focus, etc.
  1181. ----------------------------------------------------------------------------------------*/
  1182.  
  1183. void    ZAdvancedDialog::ReceiveMessage( ZComrade* aSender, long aMessage, void* msgData )
  1184. {
  1185.     switch ( aMessage )
  1186.     {
  1187.         case ItemNowHandler:
  1188.             if ( aSender != curHandler )
  1189.             {
  1190.                 // tell current handler to stop being the handler!
  1191.                 
  1192.                 if ( curHandler )
  1193.                     curHandler->BecomeHandler( FALSE );
  1194.                     
  1195.                 curHandler = (ZDialogItem*) aSender;
  1196.                 
  1197.                 ClipRect( &macWindow->portRect );
  1198.                 PenNormal();
  1199.                 ForeColor( blackColor );
  1200.                 
  1201.                 if ( curHandler )
  1202.                 {
  1203.                     // if there is a new item handler, make sure any editable text
  1204.                     // is not hilited
  1205.                     
  1206.                     HiliteDialogText( FALSE );
  1207.                     
  1208.                     focusItem = curHandler->GetID();
  1209.                     ((DialogPeek) macWindow)->editField = 0;
  1210.                 }
  1211.                 else
  1212.                 {
  1213.                     focusItem = 0;
  1214.                     HiliteDialogText( TRUE );
  1215.                 }
  1216.                 gCurHandler = GetHandler();
  1217.             }
  1218.             break;
  1219.         case ItemNoLongerHandler:
  1220.             curHandler = NULL;
  1221.             gCurHandler = GetHandler();
  1222.             break;
  1223.         default:
  1224.             inherited::ReceiveMessage( aSender, aMessage, msgData );
  1225.             break;
  1226.     }
  1227. }
  1228.  
  1229. /*-----------------------------***  HILITEDIALOGTEXT  ***-------------------------------*/
  1230. /*    
  1231. set up hiliting of edit text items in the dialog
  1232. ----------------------------------------------------------------------------------------*/
  1233.  
  1234. void    ZAdvancedDialog::HiliteDialogText( Boolean state )
  1235. {
  1236.     // turn on/off any editable text hiliting
  1237.     
  1238.     DialogPeek    dp = (DialogPeek) macWindow;
  1239.     
  1240.     if ( dp->textH )
  1241.     {
  1242.         if ( state )
  1243.             TEActivate( dp->textH );
  1244.         else
  1245.         {
  1246.             TEDeactivate( dp->textH );
  1247.             TESetSelect( 32767, 32767, dp->textH );
  1248.         }
  1249.     }
  1250. }
  1251.  
  1252.  
  1253. /*------------------------------***  SELECTNEXTFOCUS  ***-------------------------------*/
  1254. /*    
  1255. this is a cunning routine that allows us to sneak our extra focussable items into the
  1256. normal tabbing behaviour. Items that can have the focus are selected in turn each time
  1257. this is called. If the item is a standard edit field, this returns FALSE. If it's one
  1258. of ours, it returns TRUE. This boolean is passed back from the Filter function so that
  1259. it controls whether the dialog manager helps us out or not.
  1260. ----------------------------------------------------------------------------------------*/
  1261.  
  1262. Boolean        ZAdvancedDialog::SelectNextFocus()
  1263. {
  1264.     DialogPeek        dp;
  1265.     short            curFocus, totalItems, n, iType, i;
  1266.     ZDialogItem*    di;
  1267.     Boolean            found = FALSE;
  1268.     Handle            iHand;
  1269.     Rect            iBox;
  1270.     
  1271.     dp = (DialogPeek) macWindow;
  1272.     totalItems = CountDITL( macWindow );
  1273.     
  1274.     // what item currently has the focus?
  1275.     
  1276.     if ((focusItem == 0) && HasEditFields())
  1277.         curFocus = dp->editField + 1;
  1278.     else
  1279.         curFocus = focusItem;
  1280.         
  1281.     n = curFocus + 1;
  1282.     i = 0;
  1283.         
  1284.     // find the next item to this one eligible to have the focus. This is either an
  1285.     // edit field or an object item that returns TRUE for CanBeHandler.
  1286.     
  1287.     do
  1288.     {
  1289.         // if we got to the end, start over at item 1
  1290.         
  1291.         if ( n > totalItems )
  1292.             n = 1;
  1293.         
  1294.         // is there an object at this position?
  1295.         
  1296.         di = GetObjectItem( n );
  1297.         
  1298.         if (di && di->CanBeHandler())
  1299.         {
  1300.             // yes, and it can be the focus!!
  1301.         
  1302.             found = TRUE;
  1303.             break;
  1304.         }
  1305.         else
  1306.         {
  1307.             // no. Is there an edit text item in this position?
  1308.         
  1309.             GetDialogItem( macWindow, n, &iType, &iHand, &iBox );
  1310.             
  1311.             if (( iType & 0x7F ) == editText )
  1312.             {
  1313.                 // yes, so we'll let the Dialog Manager set the focus on this one
  1314.                 
  1315.                 found = TRUE;
  1316.                 break;
  1317.             }
  1318.         }
  1319.         
  1320.         n++;    // try the next item
  1321.     }
  1322.     while( !found && ( ++i < totalItems )); 
  1323.     
  1324.     // if we found an item to be focussed, set that focus now
  1325.     
  1326.     if ( found )
  1327.         SelectItem( n );
  1328.     
  1329.     // if an object, we handled it...
  1330.     
  1331.     return ( focusItem != 0 );
  1332. }
  1333.  
  1334.  
  1335. /*------------------------------***  HASMULTIPLEFOCI  ***-------------------------------*/
  1336. /*    
  1337. this returns TRUE if this dialog has more than 1 item that can be the keyboard focus. 
  1338. ----------------------------------------------------------------------------------------*/
  1339.  
  1340. Boolean        ZAdvancedDialog::HasMultipleFoci()
  1341. {
  1342.     short            iType, fCount = 0, n;
  1343.     Handle            iHand;
  1344.     Rect            iBox;
  1345.     ZDialogItem*    di;
  1346.     
  1347.     n = CountDITL( macWindow );
  1348.     
  1349.     do
  1350.     {
  1351.         di = GetObjectItem( n );
  1352.         
  1353.         if (di && di->CanBeHandler())
  1354.             fCount++;
  1355.         else
  1356.         {
  1357.             GetDialogItem( macWindow, n, &iType, &iHand, &iBox );
  1358.         
  1359.             if (( iType & 0x7F ) == editText )
  1360.                 fCount++;
  1361.         }
  1362.     }
  1363.     while( --n );
  1364.     
  1365.     return ( fCount > 1 );
  1366. }
  1367.  
  1368.